home *** CD-ROM | disk | FTP | other *** search
- ; SPFONT - software fonts for Sprint
- ;
- ; Copyright (c) 1989 Andrew D. Morrow
- ;
- ; =======================================================================
- ;
- ; Edit History:
- ;
- ; jan89, v0.1; called HERC, released to Borland for comments
- ; 6mar89, v0.2; renamed SPFONT, supports HERC and CGA, implemented as TSR
- ; 13mar89, v0.2a; support video function 14 (WriteTTY); @StringInput uses it.
- ; 29mar89, v0.2b; experiment with loading font file from command line
- ; 29mar89, v0.2c; forget 2b. install font table with SPFONTLD
- ; 31mar89, v0.2d; detect Hercules card
- ; 1apr89, v0.2e; expand fonttable to 8 fonts
- ; add word-underline attribute
- ; 6apr89, v0.2f; if /p, fonts 1-7 will print reverse video
- ;--- first Compuserve release
- ; 17apr89, v0.2g; fix scroll-down bug (FirstScanTable needed sentinel entry)
- ; 29apr89, v0.3; support different character heights (up to 16 dots)
- ; 3may89, v0.3a; detect SPFONT already installed, add /u uninstall option
- ; 9may89, v0.3b; add /b non-blinking cursor option
- ; add blink & huge cursor video functions
- ; 10may89, v0.3c; /a option makes CGA cursor sizing look ok with large chars
- ; 17may89, v0.3d; don't display cursor when positioned off the screen
- ; 24jun89, v0.3e; /k option for block cursor at startup
- ; remove chars 0-31 from each font (since Sprint can't
- ; use them) -- a savings of 2K-3K
- ; don't display characters when cursor is off-screen either
- ; 25jun89, v0.3f; jump to video functions through an address table
- ; blink & huge cursor routines return original value
- ;--- second Compuserve release
- ;
- ; =======================================================================
- ;
- ; Future Enhancements:
- ;
- ; - /s option prevents snow on CGA
- ; - /m# user-defined video mode number (if SPFONTMODE chokes other TSRs)
- ; - /1-/7 options to choose fewer fonts in memory (/1=/p; missing fonts
- ; would be replaced by reverse video)
- ; - EGA/VGA support
- ;
- ;
- ; =======================================================================
- ;
- DOSSEG
- .MODEL TINY
-
- ; ***********************************************************************
- ; * *
- ; * Equates *
- ; * *
- ; ***********************************************************************
-
- SPFONTMODE EQU 255 ; a unique video mode number
- PREVIOUSMODE EQU 254
-
- MAXFONTS EQU 8 ; number of fonts supported
- CHARSPERFONT EQU 224 ; from 20h to FFh
-
- ; The following equates identify the differences between HERC and CGA
- ; video memory. Hopefully they will adequately describe EGA and VGA as well.
- ;
- IFDEF HERC
- VIDEOBASE EQU 0b000h ; video page 0
- MAXBANKS EQU 4
- BANKSIZE EQU 02000h
- MAXSCANLINES EQU 348
- MAXCOLUMNS EQU 90
- ELSEIFDEF CGA
- VIDEOBASE EQU 0b800h
- MAXBANKS EQU 2
- BANKSIZE EQU 02000h
- MAXSCANLINES EQU 200
- MAXCOLUMNS EQU 80
- ELSE
- ERR missing /dHERC or /dCGA on TASM command line!
- ENDIF
-
- MAXROWS EQU (MAXSCANLINES/CHARHEIGHT)
-
- ; Values for Attribute bits. The bit-mapping bears no relation to standard
- ; IBM video attribute bytes. The only file that has to know about this
- ; mapping is MAIN.SPL which maps typestyle to video attribute. Unfortunately,
- ; if Sprint is started without SPFONT in memory, plain characters will
- ; be "inivsible". The unused bits (4,3,2) are available for future fonts.
- ;
- RV EQU 80h ; reverse video
- UN EQU 40h ; underline
- WU EQU 20h ; word underline
- SO EQU 10h ; strikeout
- ATTRIBMASK EQU 07h ; up to MAXFONTS fonts
-
- ; Bios interrupts called
- ;
- VIDEOINT EQU 10h
- TIMERINT EQU 1ch
- ENDINT EQU 20h
- DOSINT EQU 21h
- TSRINT EQU 27h
-
- ; Video functions supported. Internally, Sprint only calls functions 2,6,7 & 9.
- ; The init & reset strings in the terminal driver call function 0. Functions
- ; 1,3 & 15 have been implemented to support "hardware" macros that try to
- ; detect a Mono or Color monitor, or that want to manipulate the cursor shape.
- ; Functions 14 (and indirectly, 8) are used by @StringInput who uses DOS
- ; calls for character i/o so that SPFMT is as DOS-compatible as possible.
- ; Two non-standard cursor control functions are also supported; from a Sprint
- ; "hardware" string, cursor blink can be turned on & off and the cursor can
- ; be turned into a huge "cross" for better visibility.
- ;
- SETVID EQU 0
- SETCSIZE EQU 1
- SETCPOS EQU 2
- GETCINFO EQU 3 ; returns cx,dx
- SCROLLUP EQU 6
- SCROLLDN EQU 7
- GETCHAR EQU 8 ; returns ax
- WRTCHAR EQU 9
- WRITETTY EQU 14
- GETVID EQU 15 ; returns ax,bx
-
- SETBLINK EQU 16
- SETCROSS EQU 17
-
- ; DOS functions called (by the initialization code only!)
- PUTSTR EQU 9
- GETVECT EQU 35h
- SETVECT EQU 25h
- DEALLOC EQU 49h
-
- ; States of the simulated cursor. The CBUSY state protects the cursor
- ; from being manipulated by a timer interrupt when a video function is
- ; in progress.
- COFF EQU 0
- CON EQU 1
- CBUSY EQU 2
-
- ; ***********************************************************************
- ; * *
- ; * Entry point *
- ; * *
- ; ***********************************************************************
-
- .CODE
- ORG 100h
- Start: jmp Init
- DW FontAnchor ; so that SPFONTLD can find table
-
- ; ***********************************************************************
- ; * *
- ; * Data Variables *
- ; * *
- ; ***********************************************************************
-
- ; Note: Some of the Cursor variables (Top,Bot,Row,Col) are words
- ; even though only the bottom byte is significant. Doing this
- ; makes the DrawCharacter and CursorToggle routines smaller and
- ; faster. Routines that update these variables are careful to leave
- ; the high-order byte untouched.
-
- PlainFontOnly DB 0 ; assume all fonts wanted
-
- OldVideoMode DB ?
- VideoActive DB 0 ; initially inactive
-
- OldVideoInt DD ?
- OldTimerInt DD ?
-
- CursorState DB COFF
- CursorCount DB 0 ; cursor initially disabled
-
- CursorBlink DB 1
- CursorCross DB 0
- CursorAdjust DB 0
-
- CursorTop DW CHARHEIGHT-2
- CursorBot DW CHARHEIGHT-1
-
- CursorRow DW 0
- CursorCol DW 0
-
- TempCol DW ?
- TempTop DW ?
- TempBot DW ?
-
- ScrollNLines DW ?
- ScrollFill DW ?
- ScrollUpper DW ?
- ScrollLower DW ?
- ScrollRange DW ?
-
- ; ***********************************************************************
- ; * *
- ; * Timer Handler & Cursor Routines *
- ; * *
- ; ***********************************************************************
-
- ; Turning the cursor off and on every time we write a character is a
- ; waste of precious CPU time. So turning on the cursor just sets a
- ; flag so that the timer interrupt will truly restore the cursor
- ; 50-100ms later. If more video functions are called in the meantime
- ; the timer will keep being deferred until the screen is "idle".
- ; The only problem with this scheme is that holding the up- or down-
- ; arrow to scroll can cause the cursor to apparently "disappear" until
- ; you take your finger off the key!
- ;
- ; Another advantage of hooking the timer into the cursor routines is that
- ; a blinking cursor can be simulated. The blink rate could be made
- ; programmable (or disabled for a non-blinking cursor), and the on/off
- ; cycle could be made "uneven".
-
- CursorOff:
- cmp [CursorState],CON ; if it's on ...
- jne @@cf1
- call CursorToggle ; ... turn it off
- @@cf1: mov [CursorState],COFF ; ... and note the fact
- ret
-
- CursorOn:
- mov [CursorCount],2 ; reset the timer
- ret
-
- ; Timer interrupts are generated by hardware so all registers are precious.
- ; Pushing the registers is done in two steps to that the expensive "big"
- ; push is done only when needed.
- ;
- ; The CursorCount variable is only zero when SPFONT mode is inactive.
- ; When the driver is active, a decrement to zero causes the cursor to be
- ; toggled and the variable reset to a non-zero value. The only exception
- ; to this is when the timer interrupt occurs when a video function is
- ; executing. CursorOff (via CursorToggle) has marked the cursor BUSY,
- ; and the timer interrupt will return with CursorCount at zero. When
- ; the video function is finished, CursorOn will set CursorCount non-zero
- ; and the timer will gain control again some time later.
- ;
- NewTimerHandler:
- push ax
- push ds
- mov ax,cs
- mov ds,ax
-
- cmp [CursorCount],0 ; SPFONT active?
- je TimerDone ; no, don't do anything!
-
- dec [CursorCount] ; time to toggle the cursor?
- jnz TimerDone ; not yet
-
- push es
- push di
- push bx
- push cx
- push dx
-
- cmp [CursorState],COFF ; if the cursor's off ...
- jne @@tm1
- call CursorToggle ; ... turn it on ...
- mov [CursorState],CON
- mov [CursorCount],4 ; for about half a second
- jmp @@tm2
- @@tm1:
- cmp [CursorState],CON ; if it's on ...
- jne @@tm2
- cmp [CursorBlink],0 ; ... and blinking is wanted ...
- je @@tm2
- call CursorToggle ; ... do the opposite
- mov [CursorState],COFF
- mov [CursorCount],4
- @@tm2:
- pop dx
- pop cx
- pop bx
- pop di
- pop es
-
- TimerDone:
- pop ds
- pop ax
- jmp cs:[OldTimerInt] ; service other timers
-
- CursorToggle:
- mov [CursorState],CBUSY ; so a timer interrupt won't interfere
- ;
- cmp [CursorCross],0 ; is a huge cursor wanted?
- je @@ct9
- ;
- xor bx,bx ; yes - toggle the whole row ...
- mov cx,MAXCOLUMNS
- @@ct1: push cx
- push bx
- mov ax,[CursorRow]
- xor cx,cx
- mov dx,CHARHEIGHT-1
- call DisplayCursor
- pop bx
- inc bx
- pop cx
- loop @@ct1
- ;
- xor ax,ax ; ... and the whole column ...
- mov cx,MAXROWS
- @@ct2: push cx
- push ax
- mov bx,[CursorCol]
- xor cx,cx
- mov dx,CHARHEIGHT-1
- call DisplayCursor
- pop ax
- inc ax
- pop cx
- loop @@ct2
- ;
- @@ct9: mov ax,[CursorRow] ; ... and/or just the exact position
- mov bx,[CursorCol]
- mov cx,[CursorTop]
- mov dx,[CursorBot]
- jmp DisplayCursor
-
- ; ax=row, bx=col, cx=top, dx=bot
- DisplayCursor:
- cmp ax,MAXROWS ; don't display cursor off the screen
- jge @@dcdone
- cmp bx,MAXCOLUMNS
- jge @@dcdone
- ;
- mov [TempCol],bx
- mov [TempTop],cx
- mov [TempBot],dx
- ;
- IF CHARHEIGHT EQ 8
- shl ax,1 ; convert row -> ScanTable index in bx
- shl ax,1
- shl ax,1
- ELSE
- mov ah,CHARHEIGHT
- mul ah
- ENDIF
- add ax,[TempTop]
- shl ax,1
- mov bx,ax
- ;
- mov ax,VIDEOBASE
- mov es,ax
- ;
- mov cx,[TempBot]
- sub cx,[TempTop]
- inc cx
- @@dcloop:
- mov dx,[ScanTable+bx]
- add dx,[TempCol]
- mov di,dx
- mov al,es:[di]
- xor al,0ffh
- mov es:[di],al
- add bx,2
- loop @@dcloop
- @@dcdone:
- ret
-
- ; ***********************************************************************
- ; * *
- ; * Video Handler *
- ; * *
- ; ***********************************************************************
-
- ; Sprint is written in TurboC (I would hope!) and the manuals state
- ; that ax..dx and es are fair game, so we only have to save ds,si & di.
- ; Unfortunately, some TSR programs have been found that do not adequately
- ; save their registers during video calls, so in the interests of safety,
- ; all registers are saved.
- ;
- ; The low level routines DrawCharacter and CursorToggle destroy most
- ; registers so it is up to the video function to save any important
- ; registers.
- ;
- NewVideoHandler:
- cmp cs:[VideoActive],0ffh ; are we in SPFONT mode?
- je ExecFunction ; yes
- cmp ax,(SETVID*256)+SPFONTMODE ; switching to SPFONT mode?
- je ExecFunction ; yes
- jmp cs:[OldVideoInt] ; else pass onto old handler
-
- ExecFunction:
- push ds ; save important registers
- push si
- push di
- mov si,cs
- mov ds,si
- ;
- push es ; save everything else
- push dx
- push cx
- push bx
- push ax
- ;
- @@exec:
- push ax
- mov al,ah
- xor ah,ah
- sal ax,1
- mov si,ax
- pop ax
- cmp si,ExecTableLen
- jae DoNothing
- jmp [ExecTable+si]
-
- DoNothing:
- jmp ReturnVOID ; video function not supported
-
- ExecTable LABEL WORD
- dw OFFSET DoSetVideoMode
- dw OFFSET DoSetCursorSize
- dw OFFSET DoSetCursorPos
- dw OFFSET DoGetCursorInfo
- dw OFFSET DoNothing
- dw OFFSET DoNothing
- dw OFFSET DoScrollUp
- dw OFFSET DoScrollDown
- dw OFFSET DoGetCharInfo
- dw OFFSET DoWriteChar
- dw OFFSET DoNothing
- dw OFFSET DoNothing
- dw OFFSET DoNothing
- dw OFFSET DoNothing
- dw OFFSET DoWriteTTY
- dw OFFSET DoGetVideoParms
- dw OFFSET DoSetCursorBlink
- dw OFFSET DoSetCursorCross
- ExecTableLen EQU $-ExecTable
-
- DoSetVideoMode:
- cmp al,SPFONTMODE ; switch to SPFONT mode?
- jne @@sv1
- mov ah,GETVID ; yes, remember current video mode
- pushf
- call cs:[OldVideoInt]
- mov [OldVideoMode],al ; (should we also save video page?)
-
- call EnterGraphicsMode
-
- mov [CursorRow],0
- mov [CursorCol],0
- mov [CursorState],COFF
- mov [CursorCount],1 ; next timer will toggle cursor on
-
- mov cs:[VideoActive],0ffh ; SPFONT active!
- jmp ReturnVOID
-
- @@sv1: cmp al,PREVIOUSMODE ; revert to previous mode?
- jne @@sv2
- mov al,[OldVideoMode] ; yes (ah is still SETVID)
-
- @@sv2: mov [CursorCount],0 ; disable the software cursor
- mov cs:[VideoActive],0 ; no longer in SPFONT
- pushf
- call cs:[OldVideoInt] ; let the old handler do the work
- jmp ReturnVOID
-
- DoSetCursorSize:
- push cx
- call CursorOff
- pop cx
-
- cmp ch,CHARHEIGHT-1 ; make sure top is 0..CHARHEIGHT-1
- jle @@sz1
- mov ch,CHARHEIGHT-1
- @@sz1:
- cmp cl,CHARHEIGHT-1 ; make sure bottom is 0..CHARHEIGHT-1
- jle @@sz2
- mov ch,CHARHEIGHT-1
- @@sz2:
- cmp ch,cl ; make sure top <= bottom
- jle @@sz3
- xchg ch,cl
- @@sz3:
-
- IF CHARHEIGHT NE 8
- cmp [CursorAdjust],0 ; if cursor size adjusting wanted ...
- je @@sz9
- cmp cl,7 ; ... & changing CGA bottom line ...
- jne @@sz9
- cmp ch,0 ; ... then leave top line as is ...
- je @@sz8
- sub cl,ch
- mov ch,CHARHEIGHT-1
- sub ch,cl ; ... else top = (CHARHEIGHT-1)-(bottom-top)
- @@sz8: mov cl,CHARHEIGHT-1 ; ... and set new bottom line
- @@sz9:
- ENDIF
-
- mov BYTE PTR [CursorTop],ch
- mov BYTE PTR [CursorBot],cl
- call CursorOn
- jmp ReturnVOID
-
- DoSetCursorPos:
- push dx
- call CursorOff
- pop dx
- mov BYTE PTR [CursorRow],dh
- mov BYTE PTR [CursorCol],dl
- call CursorOn
- jmp ReturnVOID
-
- DoGetCursorInfo:
- mov ch,BYTE PTR [CursorTop]
- mov cl,BYTE PTR [CursorBot]
- mov dh,BYTE PTR [CursorRow]
- mov dl,BYTE PTR [CursorCol]
- jmp ReturnCXDX
-
- ; To properly determine the character/attribute under the cursor
- ; would require a lot of computation matching through the font tables.
- ; Returning just a "plain-space" seems to keep DOS perfectly happy.
- ;
- DoScrollUp:
- call ScrollPrep
- call CursorOff
- call ScrollUp
- call CursorOn
- jmp ReturnVOID
-
- DoScrollDown:
- call ScrollPrep
- call CursorOff
- call ScrollDown
- call CursorOn
- jmp ReturnVOID
-
- DoGetCharInfo:
- mov ax,' '*256+0 ; return char=space, attrib=plain
- jmp ReturnAX
-
- DoWriteChar:
- push cx
- push bx
- push ax
- call CursorOff
- pop ax
- pop bx
- pop cx
- @@wc1:
- push cx
- push bx
- push ax
- call DrawCharacter
- pop ax
- pop bx
- pop cx
- inc BYTE PTR [CursorCol]
- loop @@wc1
- call CursorOn
- jmp ReturnVOID
-
- ; Write a character as TTY. @StringInput uses it so I must support it.
- ; Fortunately for me, only 65 characters of input is allowed (so line
- ; wrap should never occur) and backspace is the only editing character.
- ; CR & LF codes are sent when Enter is pressed, but these can be ignored
- ; since the screen driver repositions the cursor anyways. Characters are
- ; always written in the "plain" attribute.
- ;
- ; entry: al=character
- DoWriteTTY:
- cmp al,0dh ; cr?
- je @@ttyend
- cmp al,0ah ; lf?
- je @@ttyend
-
- push ax
- call CursorOff
- pop ax
-
- cmp al,08h ; backspace?
- jne @@tty1
-
- cmp BYTE PTR [CursorCol],0 ; if at left edge ...
- je @@ttydone ; ... do nothing
- dec BYTE PTR [CursorCol] ; ... else just move cursor
- jmp @@ttydone
-
- @@tty1: mov bl,0 ; plain attribute
- call DrawCharacter ; print the char ...
- inc BYTE PTR [CursorCol] ; ... and move the cursor
- @@ttydone: ; common wrap-up
- call CursorOn
- @@ttyend:
- jmp ReturnVOID
-
- ; will macros with hardware strings interpret SPFONTMODE as 'Mono' or 'Color'?
- DoGetVideoParms:
- mov ax,(MAXCOLUMNS*256)+SPFONTMODE
- xor bh,bh ; always video page 0
- jmp ReturnAXBX
-
- DoSetCursorBlink:
- push ax
- call CursorOff
- pop ax
- mov ah,[CursorBlink] ; remember old value
- cmp al,2 ; if al >= 2 ...
- jl @@cbl1
- mov al,ah ; ... then leave unchanged
- @@cbl1: mov [CursorBlink],al ; store new value ...
- push ax
- call CursorOn
- pop ax
- mov al,ah ; ... and return old one
- xor ah,ah
- jmp ReturnAX
-
- DoSetCursorCross:
- push ax
- call CursorOff
- pop ax
- mov ah,[CursorCross]
- cmp al,2
- jl @@ccr1
- mov al,ah
- @@ccr1: mov [CursorCross],al
- push ax
- call CursorOn
- pop ax
- mov al,ah
- xor ah,ah
- jmp ReturnAX
-
- ReturnAX:
- pop es ; throw away saved ax
- pop bx
- pop cx
- pop dx
- pop es
- jmp VideoDone
- ReturnAXBX:
- pop es ; throw away saved ax
- pop es ; and bx
- pop cx
- pop dx
- pop es
- jmp VideoDone
- ReturnCXDX:
- pop ax
- pop bx
- pop es ; throw away saved cx
- pop es ; and dx
- pop es
- jmp VideoDone
- ReturnVOID:
- pop ax
- pop bx
- pop cx
- pop dx
- pop es
- VideoDone:
- pop di
- pop si
- pop ds
- iret
-
- ; ***********************************************************************
- ; * *
- ; * Video Subroutines *
- ; * *
- ; ***********************************************************************
-
- ; Scroll Up. Although the function is defined to work on any rectangular
- ; area of the screen, Sprint only scrolls whole lines so the column numbers
- ; in cl & dl can be ignored.
- ;
- ; The technique used here is to compute the source and destination
- ; offsets for the first bank and the length of the block in that bank,
- ; then the data is moved in each of the banks. Note that the computations
- ; have to take into account how many of the CHARHEIGHT scan lines of a character
- ; reside in a bank. We also divide the length by 2 since we want to use
- ; a movsw command to move words instead of bytes.
- ;
- ; entry: al=Nlines, bh=fill attrib, ch=Upper row, dh=Lower row
- ;
- ScrollPrep:
- xor ah,ah
- mov [ScrollNLines],ax
- mov al,ch
- mov [ScrollUpper],ax
- mov al,dh
- mov [ScrollLower],ax
-
- sub ax,[ScrollUpper]
- inc ax
- mov [ScrollRange],ax
-
- cmp [ScrollNLines],0
- je @@su4 ; if Nlines = 0 ...
- cmp ax,[ScrollNLines]
- jge @@su5 ; ... or Range < Nlines ...
- @@su4: mov [ScrollNLines],ax ; then Nlines = Range
- @@su5:
-
- xor dx,dx ; fill with zeros
- test bh,RV
- jz @@su2
- dec dx ; fill with ones
- @@su2: mov [ScrollFill],dx
- ret
-
- ScrollUp:
- cmp ax,[ScrollNLines] ; if scrolling whole range ...
- je @@sufill ; ... then bypass move
-
- ; move data in each bank
- ; compute source, destination & length for first bank
- mov ax,[ScrollUpper]
- add ax,[ScrollNLines]
- shl ax,1
- mov bx,ax
- mov si,[ScanFirstTable+bx] ; si = U+N
-
- mov ax,[ScrollUpper]
- shl ax,1
- mov bx,ax
- mov di,[ScanFirstTable+bx] ; di = U
-
- mov ax,[ScrollRange]
- sub ax,[ScrollNLines]
- shl ax,1
- mov bx,ax
- mov cx,[Rows2Words+bx] ; cx = (L-U+1)-N
-
- mov ax,VIDEOBASE
- mov es,ax
- push ds
- mov ds,ax
- cld
-
- mov bx,MAXBANKS
- @@su1:
- push cx
- push si
- push di
-
- rep movsw
-
- pop di
- pop si
- pop cx
-
- add si,BANKSIZE
- add di,BANKSIZE
-
- dec bx
- jnz @@su1
-
- pop ds
-
- ; fill empty region depending on attribute
- ; compute source & length for first bank
- @@sufill:
- mov ax,[ScrollLower]
- sub ax,[ScrollNLines]
- inc ax
- shl ax,1 ; since ScanFirst is a word table
- mov bx,ax
- mov di,[ScanFirstTable+bx] ; si = L-N+1
-
- mov ax,[ScrollNLines]
- shl ax,1
- mov bx,ax
- mov cx,[Rows2Words+bx] ; cx = N
-
- mov ax,[ScrollFill]
-
- ; es and df are unchanged from above
-
- mov bx,MAXBANKS
- @@su3:
- push cx
- push di
-
- rep stosw
-
- pop di
- pop cx
-
- add di,BANKSIZE
-
- dec bx
- jnz @@su3
-
- ret
-
- ScrollDown:
- cmp ax,[ScrollNLines] ; if scrolling whole range ...
- je @@sdfill ; ... then bypass move
-
- ; move data in each bank
- ; compute source, destination & length for first bank
- mov ax,[ScrollRange]
- sub ax,[ScrollNLines]
- add ax,[ScrollUpper]
- shl ax,1
- mov bx,ax
- mov si,[ScanFirstTable+bx] ; si = U+(R-N)
- dec si
- dec si
-
- mov ax,[ScrollLower]
- inc ax
- shl ax,1
- mov bx,ax
- mov di,[ScanFirstTable+bx] ; di = L+1
- dec di
- dec di
-
- mov ax,[ScrollRange]
- sub ax,[ScrollNLines]
- shl ax,1
- mov bx,ax
- mov cx,[Rows2Words+bx] ; cx = R-N
-
- mov ax,VIDEOBASE
- mov es,ax
- push ds
- mov ds,ax
- std
-
- mov bx,MAXBANKS
- @@sd1:
- push cx
- push si
- push di
-
- rep movsw
-
- pop di
- pop si
- pop cx
-
- add si,BANKSIZE
- add di,BANKSIZE
-
- dec bx
- jnz @@sd1
-
- pop ds
-
- ; fill empty region depending on attribute
- ; compute source & length for first bank
- @@sdfill:
- mov ax,[ScrollUpper]
- add ax,[ScrollNLines]
- shl ax,1 ; since ScanFirst is a word table
- mov bx,ax
- mov di,[ScanFirstTable+bx] ; si = U+N
- dec di
- dec di
-
- mov ax,[ScrollNLines]
- shl ax,1
- mov bx,ax
- mov cx,[Rows2Words+bx] ; cx = N
-
- mov ax,[ScrollFill]
-
- ; es and df are unchanged from above
-
- mov bx,MAXBANKS
- @@sd3:
- push cx
- push di
-
- rep stosw
-
- pop di
- pop cx
-
- add di,BANKSIZE
-
- dec bx
- jnz @@sd3
-
- ret
-
- ; The critical DrawCharacter routine. Almost any technique that make this
- ; routine faster is worth the effort. The reverse, underline and strikeout
- ; attributes could be coded into font tables but all the combinations of 5
- ; attribute bits would require (2^5)*2K = 64K of font tables! This is
- ; obviously too much.
- ;
- ; The code of the "inner loop" has been implemented as a macro loop
- ; (producing replicated code) for efficiency.
- ;
- ; Draw a character at the current row & column
- ; entry: al = character, bl=attribute
- ; uses: ax,bx,cx,dx, si,di,es
- ;
- DrawCharacter:
- test bl,WU ; word underline wanted?
- jz @@dc1
- or bl,UN ; yes - assume underline ...
- cmp al,' '
- jne @@dc1
- and bl,NOT UN ; ... except for spaces
- @@dc1:
- cmp [PlainFontOnly],0ffh ; if only plain font in memory ...
- jne @@dc2
- test bl,ATTRIBMASK ; ... and another font is wanted ...
- jz @@dc2
- and bl,NOT ATTRIBMASK ; ... then print in plain font ...
- or bl,RV ; ... with reverse video
- @@dc2:
- sub al,' ' ; adjust out unprintable characters
- jnb @@dc3
- mov al,0 ; unprintable characters print as space
- @@dc3:
- IF CHARHEIGHT EQ 8
- xor ah,ah ; convert char -> FontTable index in si
- shl ax,1 ; (*CHARHEIGHT bytes per entry)
- shl ax,1
- shl ax,1
- ELSE
- mov ah,CHARHEIGHT
- mul ah
- ENDIF
- add ax,OFFSET FontTable
- mov si,ax
- mov ax,bx ; get attribute
- and ax,ATTRIBMASK ; isolate font# bits
- mov cx,(CHARSPERFONT*CHARHEIGHT)
- mul cx ; (*?K per font)
- add si,ax ; index FontTable to desired font
- mov cx,bx ; attribute in cl
- ;
- mov ax,[CursorCol]
- cmp ax,MAXCOLUMNS
- jge @@nodraw
- mov ax,[CursorRow]
- cmp ax,MAXROWS
- jge @@nodraw
- jmp @@dc4
- @@nodraw: ret ; cursor is off-screen
-
- @@dc4: ; convert row -> ScanTable index in bx
- IF CHARHEIGHT EQ 8
- shl ax,1
- shl ax,1
- shl ax,1
- ELSE
- mov bx,CHARHEIGHT
- mul bx
- ENDIF
- shl ax,1
- mov bx,ax
- mov ax,VIDEOBASE ; es -> video memory
- mov es,ax
- cld
-
- lup = 0
- REPT CHARHEIGHT
- mov dx,[ScanTable+bx]
- add dx,[CursorCol]
- mov di,dx
- lodsb
- IF lup EQ (CHARHEIGHT/2)+1
- test cl,SO
- jz @@dc&lup&st
- mov al,0ffh
- @@dc&lup&st:
- ENDIF
- IF lup EQ (CHARHEIGHT-1)
- test cl,UN
- jz @@dc&lup&un
- mov al,0ffh
- @@dc&lup&un:
- ENDIF
- test cl,RV
- ; jz @@dc&lup&rv
- jz $+4 ; since lup is not local to REPT
- xor al,0ffh
- ;@@dc&lup&rv:
- stosb
- add bx,2
- lup = lup + 1
- ENDM
-
- ret
-
- ClearVideo:
- mov cx,(MAXBANKS*BANKSIZE)/2
- mov ax,VIDEOBASE
- mov es,ax
- xor di,di
- xor ax,ax
- cld
- rep stosw
- ret
-
- ; ***********************************************************************
- ; * *
- ; * HERC Particulars (resident) *
- ; * *
- ; ***********************************************************************
-
- IFDEF HERC
-
- CONFIGPORT EQU 3bfh
- HALFCONFIG EQU 1 ; all that SPFONT requires
-
- MODEPORT EQU 3b8h
- TEXTMODE EQU 00h
- GRAPHMODE EQU 02h
- SCREENOFF EQU 00h
- SCREENON EQU 08h
- BLINKOFF EQU 00h
- BLINKON EQU 20h
- GPAGE0 EQU 00h
- GPAGE1 EQU 80h
-
- M6845PORT EQU 3b4h
- STATUSPORT EQU 3bah
-
- graphicsreg DB 35h,2dh,2eh,7h,5bh,2h,57h,57h,2h,3h,0,0
-
- EnterGraphicsMode:
- call ClearVideo ; clear display first to avoid ugly "flash"
-
- mov al,HALFCONFIG
- mov dx,CONFIGPORT
- out dx,al
-
- mov al,GRAPHMODE+GPAGE0+SCREENON
- mov dx,MODEPORT
- out dx,al
-
- xor ah,ah ; starting with register 0
- mov cx,12 ; for 12 registers
- mov dx,M6845PORT
- mov si,OFFSET graphicsreg
- cld
- cli
- @@rv1:
- mov al,ah
- out dx,al ; select register
- inc dx
- lodsb
- out dx,al ; write value
- inc ah
- dec dx
- loop @@rv1
- sti
-
- mov al,GRAPHMODE+GPAGE0+SCREENON
- mov dx,MODEPORT
- out dx,al
- ret
-
- ENDIF
-
- ; ***********************************************************************
- ; * *
- ; * CGA Particulars *
- ; * *
- ; ***********************************************************************
-
- IFDEF CGA
-
- EnterGraphicsMode:
- mov ax,(SETVID*256)+6 ; enter 640x200 b/w mode
- pushf
- call cs:[OldVideoInt]
- ret
-
- ENDIF
-
- ; ***********************************************************************
- ; * *
- ; * Tables *
- ; * *
- ; ***********************************************************************
-
- ; This table gives the offset from VIDEOBASE for the start of each
- ; scan line. The increase in speed by avoiding multiplications
- ; is worth the space.
- ScanTable LABEL WORD
- scan = 0
- rept MAXSCANLINES/MAXBANKS
- bank = 0
- rept MAXBANKS
- DW (bank*BANKSIZE)+(scan*MAXCOLUMNS)
- bank = bank+1
- endm
- scan = scan+1
- endm
-
- ScanFirstTable LABEL WORD
- scan = 0
- rept MAXROWS+1
- DW scan*MAXCOLUMNS*(CHARHEIGHT/MAXBANKS)
- scan = scan+1
- endm
-
- Rows2Words LABEL WORD
- nrows = 0
- rept MAXROWS
- DW (nrows*MAXCOLUMNS*(CHARHEIGHT/MAXBANKS))/2
- nrows = nrows+1
- endm
-
- ; The font tables. Currently there are eight fonts implemented (plain,
- ; italic, bold, bold-italic, large, subscript, superscript & other).
- ;
- ; FontTable MUST be the last table before the installation code! If the
- ; /p option is given then all but the first (plain) font are discarded
- ; when the TSR loads. This is for the benefit of programmers who are
- ; more interested in 43 lines of text than in fonts and who want to
- ; sacrifice as little ram as possible.
- ;
- ; FontAnchor is used by SPFONTLD to assure that it is really patching a
- ; copy of SPFONT and not scribbling over just any file.
- ;
- FontAnchor DB '<SPFONT>' ; validation string
- FontTableSize DW (MAXFONTS*CHARSPERFONT*CHARHEIGHT)
- FontValid DB 0 ; set to ff by SPFONTLD
-
- FontTable LABEL BYTE
- DB (CHARSPERFONT*CHARHEIGHT) DUP (0)
- EndPlainFonts LABEL BYTE
- DB ((MAXFONTS-1)*(CHARSPERFONT*CHARHEIGHT)) DUP (0)
- EndAllFonts LABEL BYTE
-
- ; ***********************************************************************
- ; * *
- ; * Installation *
- ; * *
- ; ***********************************************************************
-
- InstallWanted DB 0 ; assume installation not wanted
-
- Herald DB 'SPFONT for '
- IFDEF HERC
- DB 'Hercules'
- ENDIF
- IFDEF CGA
- DB 'CGA'
- ENDIF
- DB ' (v0.3f) by Andrew D. Morrow','$'
-
- InstallMsg DB ' - Installed','$'
-
- UninstallMsg DB ' - Uninstalled','$'
-
- PlainMsg DB ' (Plain font only)','$'
-
- CrLfMsg DB 0dh,0ah,'$'
-
- HelpMsg DB 0dh,0ah
- DB 'Syntax: SPFONT [/options]',0dh,0ah
- DB 0dh,0ah
- DB '/i Install',0dh,0ah
- DB '/p load Plain font only (no bold,italic,etc.)',0dh,0ah
- DB '/a Adjust cursor size commands relative to CGA 0-7',0dh,0ah
- DB '/b start with nonBlinking cursor',0dh,0dh
- DB '/k start with blocK cursor',0dh,0ah
- DB '/u Uninstall',0dh,0ah
- DB '$'
-
- FontMsg DB 'Font Table not loaded. Run SPFONTLD.',0dh,0ah,'$'
-
- InMemMsg DB 'SPFONT already installed',0dh,0ah,'$'
-
- CantUnMsg DB 'Cannot Uninstall',0dh,0ah,'$'
-
- IFDEF HERC
- NoHercMsg DB 'Cannot detect Hercules Graphics Card',0dh,0ah,'$'
- ENDIF
-
- JUMPS ; I don't care how inefficient the init code is!
-
- Init:
- ; print herald
- mov dx,OFFSET Herald
- mov ah,PUTSTR
- int DOSINT
-
- ; parse command tail
- mov bx,0080h ; first 'inc bx' will skip tail length
- CmdLup: inc bx
- cmp BYTE PTR [bx],' ' ; deblank
- je CmdLup
- cmp BYTE PTR [bx],0dh ; all done?
- je EndCmd
- cmp BYTE PTR [bx],'/' ; there had better be an option
- jnz DisplayHelpMsg
- inc bx ; look at which option
- mov al,BYTE PTR [bx]
- and al,01011111b ; quick-and-dirty capitalization
- cmp al,'I'
- jz SelectInstall
- cmp al,'P'
- jz PlainFonts
- cmp al,'B'
- jz BlinkOff
- cmp al,'K'
- jz BlockCursor
- cmp al,'A'
- jz AdjustOn
- cmp al,'U'
- jz DoUninstall
- jmp DisplayHelpMsg
-
- SelectInstall:
- mov [InstallWanted],1
- jmp CmdLup
-
- PlainFonts:
- mov [PlainFontOnly],0ffh
- jmp CmdLup
-
- BlinkOff:
- mov [CursorBlink],0
- jmp CmdLup
-
- BlockCursor:
- mov [CursorTop],0
- mov [CursorBot],CHARHEIGHT-1
- jmp CmdLup
-
- AdjustOn:
- mov [CursorAdjust],0ffh
- jmp CmdLup
-
- EndCmd:
- cmp [InstallWanted],0 ; if installation was not requested
- je DisplayHelpMsg ; then assume that user needs help
-
- mov ax,(GETVECT*256)+VIDEOINT
- int DOSINT
- cmp bx,OFFSET NewVideoHandler
- mov dx,OFFSET InMemMsg
- je DisplayErrorMsg
-
- cmp [FontValid],0
- mov dx,OFFSET FontMsg
- je DisplayErrorMsg
-
- IFDEF HERC
- call DetectHercules
- mov dx,OFFSET NoHercMsg
- jz DisplayErrorMsg
- ENDIF
-
- ; replace 18.2Hz timer
- mov ax,(GETVECT*256)+TIMERINT
- int DOSINT
- mov WORD PTR cs:[OldTimerInt],bx
- mov bx,es
- mov WORD PTR cs:[OldTimerInt+2],bx
-
- mov dx,OFFSET NewTimerHandler
- push ds
- mov ax,cs
- mov ds,ax
- mov ax,(SETVECT*256)+TIMERINT
- int DOSINT
- pop ds
-
- ; replace video driver
- mov ax,(GETVECT*256)+VIDEOINT
- int DOSINT
- mov WORD PTR cs:[OldVideoInt],bx
- mov bx,es
- mov WORD PTR cs:[OldVideoInt+2],bx
-
- mov dx,OFFSET NewVideoHandler
- push ds
- mov ax,cs
- mov ds,ax
- mov ax,(SETVECT*256)+VIDEOINT
- int DOSINT
- pop ds
-
- ; deallocate environment segment
- mov ax,ds:[02ch] ; get env segment from PSP
- mov es,ax
- mov ah,DEALLOC
- int DOSINT
-
- ; acknowledge installation
- mov dx,OFFSET InstallMsg
- mov ah,PUTSTR
- int DOSINT
- cmp [PlainFontOnly],0ffh
- jne @@ai1
- mov dx,OFFSET PlainMsg
- mov ah,PUTSTR
- int DOSINT
- @@ai1:
- ; terminate & stay resident
- mov dx,OFFSET EndPlainFonts
- cmp [PlainFontOnly],0ffh
- je @@tsr
- mov dx,OFFSET EndAllFonts
- @@tsr: int TSRINT
-
- DoUninstall:
- mov ax,(GETVECT*256)+VIDEOINT ; who owns video interrupt?
- int DOSINT
- cmp bx,OFFSET NewVideoHandler ; another copy of SPFONT?
- mov dx,OFFSET CantUnMsg
- jne DisplayErrorMsg ; no - sorry
-
- mov ax,(GETVECT*256)+TIMERINT ; who owns timer interrupt?
- int DOSINT
- cmp bx,OFFSET NewTimerHandler ; another copy of SPFONT?
- mov dx,OFFSET CantUnMsg
- jne DisplayErrorMsg ; no - sorry
-
- ; at this point, es = segment address of original SPFONT
-
- ; restore video
- mov dx,WORD PTR es:[OldVideoInt]
- push ds
- mov ax,WORD PTR es:[OldVideoInt+2]
- mov ds,ax
- mov ax,(SETVECT*256)+VIDEOINT
- push es
- int DOSINT
- pop es
- pop ds
-
- ; restore timer
- mov dx,WORD PTR es:[OldTimerInt]
- push ds
- mov ax,WORD PTR es:[OldTimerInt+2]
- mov ds,ax
- mov ax,(SETVECT*256)+TIMERINT
- push es
- int DOSINT
- pop es
- pop ds
-
- ; deallocate original copy of SPFONT
- mov ah,DEALLOC
- int DOSINT
-
- ; acknowledge uninstallation
- mov dx,OFFSET UninstallMsg
- mov ah,PUTSTR
- int DOSINT
- int ENDINT
-
- DisplayHelpMsg:
- mov dx,OFFSET HelpMsg
- DisplayErrorMsg:
- push dx
- mov dx,OFFSET CrLfMsg
- mov ah,PUTSTR ; since herald doesn't crlf
- int DOSINT
- pop dx
- mov ah,PUTSTR ; print diagnostic
- int DOSINT
- int ENDINT ; and quit without doing anything
-
- ; ***********************************************************************
- ; * *
- ; * HERC Particulars (transient) *
- ; * *
- ; ***********************************************************************
-
- IFDEF HERC
-
- ; returns ax=ffff if HGC found, else 0 (Zflag set appropriately)
- DetectHercules:
- mov ah,GETVID
- int VIDEOINT
- cmp al,7 ; in mono text mode?
- jne @@dh8 ; no
-
- mov dx,STATUSPORT
- in al,dx
- and al,80h ; get current vertical retrace state
- mov ah,al ; and store it away
-
- mov cx,0ffffh ; wait a long time
- @@dh1: in al,dx
- and al,80h ; get vertical retrace state again
- cmp ah,al ; any change?
- jne @@dh9 ; yes - it's an HGC!
-
- loop @@dh1 ; no - keep testing
-
- @@dh8: xor ax,ax ; return failure
- ret
-
- @@dh9: or ax,0ffffh ; return success
- ret
-
- ENDIF
-
- END Start
-